/*
 * htsmsg.c
 *
 *  Created on: Feb 7, 2012
 *      Author: nickolaev
 */

#include "debug.h"
#include "hts.h"
#include "htsmsg.h"

static HTSMSG_FIELD emptyField = {
                .type = HTSMSG_FIELD_NONE,
                .name_len = 0,
                .data_len = 0,
                .field = { } };

/*
 * Init a new message from buffer
 */
HTSMSG* htsmsgFromBuffer(char* buffer) {

        size_t offset = 0;
        size_t idx = 0;
        size_t num_fields = 0;
        HTSMSG_FIELD* field = NULL;

        HTSMSG* result = NULL;
        HTSMSG_ROOT* root = (HTSMSG_ROOT*) buffer;

        /* Normalize the root len */
        root->len = ntohl(root->len);

        /* Normalize the field lengths */
        while (offset < root->len) {
                field = (HTSMSG_FIELD*) &(root->fields[offset]);

                field->data_len = ntohl(field->data_len);

                offset += sizeof(HTSMSG_FIELD);
                offset += field->name_len;
                offset += field->data_len;
                num_fields++;
        }

        /* Allocate the whole space needed to store htsmsg */
        result = malloc(sizeof(HTSMSG) + num_fields * sizeof(HTSMSG_FIELD*));

        if (!result)
                return NULL;

        result->root = root;
        result->num_fields = num_fields;

        offset = 0;
        while (offset < root->len) {
                field = (HTSMSG_FIELD*) &(root->fields[offset]);

                result->fields[idx] = field;

                offset += sizeof(HTSMSG_FIELD);
                offset += field->name_len;
                offset += field->data_len;
                idx++;
        }

        return result;
}

/*
 * Determine whether the messaeg was generated or converted from buffer
 */
int htsmsgIsGenerated(HTSMSG* msg) {
        int result = 0;
        if (msg->num_fields > 0
                        && (char*) msg->fields[0] != msg->root->fields) {
                result = 1;
        }
        return result;
}

void htsmsgFree(HTSMSG* msg) {
        if (msg) {
                /* Check whether this message was generated */
                if (htsmsgIsGenerated(msg)) {

                        size_t idx = 0;

                        /* Free all fields */
                        for (; idx < msg->num_fields; idx++) {
                                if (msg->fields[idx]
                                && msg->fields[idx] != &emptyField) {
                                        free(msg->fields[idx]);
                                }
                        }

                        free(msg->root);
                }

                free(msg);
        }
}

/*
 * Create new message given number of fields
 */
HTSMSG* htsmsgNewMessage(size_t num_fields) {
        HTSMSG* result = NULL;
        size_t idx = 0;

        /* Allocate the whole space needed to store htsmsg */
        result = malloc(sizeof(HTSMSG) + num_fields * sizeof(HTSMSG_FIELD*));

        result->root = NULL;
        result->num_fields = num_fields;
        for (; idx < num_fields; idx++) {
                result->fields[idx] = &emptyField;
        }

        return result;
}

#if 0
/*
 * Add new field to a created message
 */
int htsmsgAddNewField(HTSMSG* message, HTSMSG_FIELD* field) {
        size_t idx = 0;
        int result = HTS_ERROR;

        /* find empty place */
        for (; idx < message->num_fields; idx++) {
                if (message->fields[idx] == &emptyField) {
                        message->fields[idx] = field;
                        result = HTS_OK;
                }
        }

        return result;
}
#endif

HTSMSG_FIELD* htsmsgNewField(uint8 type, const char* name, uint32 value_len,
                const void* value) {

        HTSMSG_FIELD* result = NULL;
        size_t  name_len = 0;

        switch (type) {
        case HTSMSG_FIELD_NONE:
        case HTSMSG_FIELD_MAP:
                break;
        case HTSMSG_FIELD_S64:
        case HTSMSG_FIELD_STR:
        case HTSMSG_FIELD_BIN:
                name_len = strlen(name);
                result = malloc(sizeof(HTSMSG_FIELD) +
                                name_len + value_len);
                result->type = type;
                result->name_len = name_len;
                result->data_len = value_len;
                memcpy(result->field, name, name_len);
                memcpy(result->field+name_len,
                                value, result->data_len);
                break;

        case HTSMSG_FIELD_LIST:
                break;
        }

        DEBUG( if (!result) {
                LOG("Not supported type %s\n", htsmsgFieldTypeFromCode(type));
                }
        )
        return result;
}

const char* htsmsgFieldTypeFromCode(uint8 type) {
        const char* result = "UNKNOWN";

        switch (type) {
        case HTSMSG_FIELD_NONE:
                result = "NONE";
                 break;
        case HTSMSG_FIELD_MAP:
                result = "MAP";
                break;
        case HTSMSG_FIELD_S64:
                result = "S64";
                break;
        case HTSMSG_FIELD_STR:
                result = "STR";
                break;
        case HTSMSG_FIELD_BIN:
                result = "BIN";
                break;
        case HTSMSG_FIELD_LIST:
                result = "LIST";
                break;
        }

        return result;
}

/*
 *
 */
int htsmsgFieldsToBuffer(HTSMSG* msg) {
        int result = HTS_OK;
        size_t buffer_len = 0;
        size_t offset = 0;
        size_t num_fields = 0;
        size_t idx = 0;

        /* iterate over all fields and count the needed buffer length*/
        for(;idx<msg->num_fields;idx++) {
                HTSMSG_FIELD* field = msg->fields[idx];
                if (field != &emptyField) {
                        buffer_len += (field->name_len +
                                       field->data_len + sizeof(HTSMSG_FIELD));
                        num_fields++;
                }
        }

        if (msg->root) {
                free(msg->root);
        }
        msg->root = malloc(sizeof(HTSMSG_ROOT) + buffer_len);

        for(idx=0;idx<msg->num_fields;idx++) {
                HTSMSG_FIELD* field = msg->fields[idx];
                char* p_dest = msg->root->fields + offset;
                if (field != &emptyField) {
                        size_t len = (field->name_len +
                                       field->data_len + sizeof(HTSMSG_FIELD));
                        uint32* p_data_len = NULL;

                        memcpy(p_dest, field, len);
                        p_data_len = (uint32*)(p_dest + offsetof(HTSMSG_FIELD,
                                                data_len));
                        *p_data_len = htonl(*p_data_len);
                        offset += len;
                }
        }

        msg->root->len = offset;

        return result;
}
